﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NeuralNetworks.ActivationFunctions;
using NeuralNetworks.Layers;
using NeuralNetworks.Learning;
using System.ComponentModel;

namespace NeuralNetworks.Networks
{
    class SomNetwork : Network
    {
        private ActivationLayer _inputLayer;
        private KohonenLayer _kohonenLayer;

        protected double[][] _patterns;

        private int _patternSize;
        private int _nPatterns;

        protected int _epochs;

        public Layer InputLayer { get { return _inputLayer; } }
        public KohonenLayer KohonenLayer { get { return _kohonenLayer; } }
        public int Epochs { get { return _epochs; } }

        public SomNetwork(int nPatterns, string[] patterns, int kohXSize, int kohYSize)
        {
            _nPatterns = nPatterns;

            // Format patterns
            for (int i = 0; i < nPatterns; i++)
            {
                patterns[i] = patterns[i].Replace(Environment.NewLine, "");
            }
            _patternSize = patterns[0].Length;
            for (int i = 0; i < nPatterns; i++)
            {
                if (patterns[i].Length != _patternSize)
                {
                    MessageBox.Show("Błąd! Podano złe dane wejściowe.");
                    return;
                }
            }

            // Create layers
            _inputLayer = new ActivationLayer(_patternSize, new IdentityFunction(), "I");
            _kohonenLayer = new KohonenLayer(kohXSize, kohYSize, 0, 0, nPatterns);

            // Connect layers
            _kohonenLayer.ConnectWithLayer(_inputLayer);
            
            // Initialize _patterns
            _patterns = new double[patterns.Length][];

            for (int i = 0; i < nPatterns; i++)
            {
                _patterns[i] = new double[_patternSize];
                for (int j = 0; j < _patternSize; j++)
                {
                    _patterns[i][j] = Double.Parse(patterns[i][j].ToString());
                }
            }
                
        }

        public override void RandomWeights(double min, double max)
        {
            Random rand = new Random();
            _inputLayer.RandomWeights(rand);
            _kohonenLayer.RandomWeights(rand);
        }

        public override void ZeroWeights()
        {
            Random rand = new Random();
            _inputLayer.ZeroWeights();
            _kohonenLayer.ZeroWeights();
        }

        virtual public void Learn(SortedList<int, LearningProperty> learningParams, int epochs)
        {

            LearningProperty currentProp = getCurrentProperty(learningParams);

            _kohonenLayer.NeighbourhoodDist = currentProp.NeighbourhoodDist;
            _kohonenLayer.LearningFactor = currentProp.LearningRatio;
            _kohonenLayer.Conscience = currentProp.Conscience;

            
            for(int i=1; i<=epochs; i++)
            {
                
                for (int p = 0; p < _nPatterns; p++)
                {
                    // set pattern
                    _inputLayer.SetOutputs(_patterns[p]);

                    _kohonenLayer.Compute();

                    //int winI = _kohonenLayer.GetWinner();

                    _kohonenLayer.Learn(i);
                }
                _epochs++;
                int pIndex = learningParams.IndexOfKey(_epochs);
                if (pIndex > -1) //trzeba przelaczyc sie na nowe parametry
                {
                    try
                    {
                        currentProp = learningParams[learningParams.Keys[pIndex + 1]];
                        Console.WriteLine(currentProp);
                        _kohonenLayer.NeighbourhoodDist = currentProp.NeighbourhoodDist;
                        _kohonenLayer.LearningFactor = currentProp.LearningRatio;
                        _kohonenLayer.Conscience = currentProp.Conscience;
                    }
                    catch (ArgumentOutOfRangeException e)
                    {
                    }
                }
            }
            
        }

        protected LearningProperty getCurrentProperty(SortedList<int, LearningProperty> learningParams)
        {
            int paramIndex = 0;
            foreach (int e in learningParams.Keys)
            {
                paramIndex = e;
                if (_epochs <= e) break;
            }

            LearningProperty currentProp = learningParams[paramIndex];
            Console.WriteLine(currentProp);
            return currentProp;
        }

        override public double[] Compute(double[] input)
        {
            _inputLayer.SetOutputs(input);

            _kohonenLayer.Compute();

            return _kohonenLayer.Outputs();
        }

    }
}
